# -*- coding: utf-8 -*-
# @Author	: starview.guojian.ma
# @Date		: 2021/11/10 22:15
# @Last Modified by:   starview.guojian.ma
# @Last Modified time: 2021/11/10 22:15
# Thanks for your comments!

import abc
import logging

from src.basedata.domain import repositories
from src.basedata.infrastructure.client.mongodb.app_administration_mapping import \
    MotorAppAdministrationMappingRepository

from src.basedata.infrastructure.client.mongodb.app_config import MotorAppConfigRepository
from src.basedata.infrastructure.client.mongodb.app_freq_mapping import MotorAppFreqMappingRepository
from src.basedata.infrastructure.client.mongodb.app_medicine_lib_mapping import MotorAppMedicineLibraryMappingRepository
from src.basedata.infrastructure.client.mongodb.department import MotorDepartmentRepository
from src.basedata.infrastructure.client.mongodb.know_icd10_mapping import MotorKnowICD10MappingRepository
from src.basedata.infrastructure.client.mongodb.medicine_library import MotorMedicineLibraryRepository
from src.basedata.infrastructure.client.mongodb.medicine_library_list import MotorMedicineLibraryListRepository
from src.basedata.infrastructure.client.mongodb.person import MotorPersonRepository

logger = logging.getLogger(__name__)


class AbstractUnitOfWork(abc.ABC):
    app_config: repositories.AppConfigAbstractRepository
    app_freq_map: repositories.AppFreqMappingAbstractRepository
    app_administration_map: repositories.AppAdministrationMappingAbstractRepository
    department: repositories.DepartmentAbstractRepository
    person: repositories.PersonAbstractRepository
    icd10: repositories.KnowledgeICD10MappingAbstractRepository
    medicine_list: repositories.MedicineLibraryListAbstractRepository
    medicine_lib: repositories.MedicineLibraryAbstractRepository
    app_med_lib_map: repositories.AppMedicineLibraryMappingAbstractRepository

    def __enter__(self):
        return self

    async def __aenter__(self):
        return self

    def __exit__(self, *args):
        pass

    async def __aexit__(self, *args):
        pass

    @abc.abstractmethod
    async def commit(self):
        raise NotImplementedError

    @abc.abstractmethod
    async def rollback(self):
        raise NotImplementedError

    @abc.abstractmethod
    def close(self):
        pass


class PersistenceUnitOfWorkFactory:
    def __init__(self, mongo_client, redis_client):
        self._closed = False
        self._mongo_client = mongo_client
        self._redis_client = redis_client

    def uow(self):
        return PersistenceUnitOfWork(self._mongo_client, self._redis_client)

    def close(self):
        if not self._closed:
            self._closed = True

    def __del__(self):
        self.close()


class PersistenceUnitOfWork(AbstractUnitOfWork):
    def __init__(self, mongo_client, redis_client):
        self._commit_flag = False
        self._initialized = False
        self._closed = False
        self._mongo_cline = mongo_client
        self._redis_client = redis_client

    async def initialize(self):
        return True

    async def __aenter__(self):
        self._commit_flag = False
        self.app_config = MotorAppConfigRepository(self._mongo_cline)
        self.app_freq_map = MotorAppFreqMappingRepository(self._mongo_cline)
        self.app_administration_map = MotorAppAdministrationMappingRepository(self._mongo_cline)
        self.department = MotorDepartmentRepository(self._mongo_cline)
        self.person = MotorPersonRepository(self._mongo_cline, self._redis_client)
        self.icd10 = MotorKnowICD10MappingRepository(self._mongo_cline)
        self.medicine_list = MotorMedicineLibraryListRepository(self._mongo_cline, self._redis_client)
        self.medicine_lib = MotorMedicineLibraryRepository(self._mongo_cline)
        self.app_med_lib_map = MotorAppMedicineLibraryMappingRepository(self._mongo_cline)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        try:
            await self.commit()
        finally:
            self._tidb_conn_dict = {}

    async def commit(self):
        if not self._commit_flag:
            self._commit_flag = True

    async def rollback(self):
        if not self._commit_flag:
            self._commit_flag = True

    def close(self):
        pass

    def __del__(self):
        self.close()
